/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2007 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author: xuefeng <php@xuefeng.org>                                    |
  +----------------------------------------------------------------------+
*/

/* $Id: header,v 1.16.2.1.2.1 2007/01/01 19:32:09 iliaa Exp $ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "ext/standard/php_smart_str.h"
#include "php_network.h"
#include "php_whois.h"
#include "php_whois_servers.h"

#ifdef PHP_WIN32
#include "win32/time.h"
#else
#include <sys/time.h>
#endif

/* {{{ whois_functions[]
 *
 * Every user visible function must have an entry in whois_functions[].
 */
zend_function_entry whois_functions[] = {
	PHP_FE(whois,		NULL)
	PHP_FE(whois_help,	NULL)
	{NULL, NULL, NULL}	/* Must be the last line in whois_functions[] */
};
/* }}} */

/* {{{ whois_module_entry
 */
zend_module_entry whois_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
	STANDARD_MODULE_HEADER,
#endif
	"whois",
	whois_functions,
	PHP_MINIT(whois),
	PHP_MSHUTDOWN(whois),
	NULL,
	NULL,
	PHP_MINFO(whois),
#if ZEND_MODULE_API_NO >= 20010901
	WHOIS_VERSION,
#endif
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_WHOIS
ZEND_GET_MODULE(whois)
#endif

/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(whois)
{
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(whois)
{
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(whois)
{
	php_info_print_table_start();
	php_info_print_table_header(2, "whois support", "enabled");
	php_info_print_table_row(2, "whois version", WHOIS_VERSION);
	php_info_print_table_end();
}
/* }}} */

static int whois_map_server(char *domain, unsigned int length)
{
	int	 i = 0,  n = 0;
	char *p = NULL;

	if (length && (length <= MAX_QUERY_LENGTH)) {
		for (p = domain; p < p + MAX_QUERY_LENGTH; p++,n++) {
			if (!(isalnum((unsigned char)*p) || *p == '.' || *p == '-')) {
				break;
			}
		}
	}

	if (n == length) {
		if (NULL != (p = zend_memrchr(domain, '.', length))) {
			n = strlen(++p);
			if (0 < n && 7 > n) {
				if (isdigit((unsigned char)*p)) {
					return 0;
				} else {
					n = sizeof(whois_servers) / sizeof(whois_servers[0]);
					for (i = 5; i < n; i++) {
						if (0 == strcmp(p, whois_servers[i].suffix)) {
							return i;
						}
					}
				}
			}
		}
	}

	return -1;
}

static smart_str whois_query(const char *server, zval **query TSRMLS_DC)
{
	char		buf[PHP_SOCK_CHUNK_SIZE];
	php_stream	*stream;
	smart_str	result;
	size_t		n;

	memset(&result, 0, sizeof(result));

	if (NULL == server || MAX_QUERY_LENGTH < Z_STRLEN_PP(query)) {
		return result;
	}

	if (NULL == (stream	= php_stream_sock_open_host(server, 43, SOCK_STREAM, NULL, NULL))) {
		return result;
	}

	if (PHP_STREAM_OPTION_RETURN_ERR == php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, 0, NULL)) {
		php_stream_close(stream);
		return result;
	}

	if (php_stream_printf(stream TSRMLS_CC, "%s\r\n", Z_STRVAL_PP(query))) {

		memset(&buf, 0, sizeof(buf));
		while (!php_stream_eof(stream)) {
			if (n = php_stream_read(stream, buf, sizeof(buf))) {
				smart_str_appendl(&result, buf, n);
				memset(&buf, 0, sizeof(buf));
			}
		}
		php_stream_close(stream);

		smart_str_0(&result);
	}

	return result;
}

static char* whois_get_referral(char *str)
{
	char	*p	= NULL;
	int		len = 0;

	if (NULL != str) {
		if (NULL != (p = strstr(str, REFERRAL_SERVER_FLAG))) {
			p += sizeof(REFERRAL_SERVER_FLAG) - 1;
			for (; p < p + MAX_QUERY_LENGTH; p++,len++) {
				if (!(isalnum((unsigned char)*p) || *p == '.' || *p == '-')) {
					return estrndup(p - len, len);
				}
			}
		}
	}

	return NULL;
}

PHP_FUNCTION(whois)
{
	int				i;
	char			*p;
	zval			**query, **item, *new_value;
	zend_bool		quick_query = 0;
	smart_str		result;
	HashPosition	pos;

	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &query, &quick_query)) {
		WRONG_PARAM_COUNT;
		return;
	}

	if (IS_STRING == Z_TYPE_PP(query)) {

		if (i = whois_map_server(Z_STRVAL_PP(query), Z_STRLEN_PP(query))) {
			result = whois_query(whois_servers[i].whois_server, query TSRMLS_CC);

			if (!quick_query) {
				if (NULL != (p = whois_get_referral(result.c))) {
					smart_str_free(&result);
					result = whois_query(p, query TSRMLS_CC);
					efree(p);
				}
			}
			ZVAL_STRINGL(return_value, result.c, result.len, 0);
		}

	} else if (IS_ARRAY == Z_TYPE_PP(query)) {

		array_init(return_value);

		for(zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(query), &pos);
			SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(query), (void**) &item, &pos);
			zend_hash_move_forward_ex(Z_ARRVAL_PP(query), &pos)
		) {
			if (IS_STRING != Z_TYPE_PP(item)) { continue; }

			if (i = whois_map_server(Z_STRVAL_PP(item), Z_STRLEN_PP(item))) {
				result = whois_query(whois_servers[i].whois_server, item TSRMLS_CC);

				if (!quick_query) {
					if (NULL != (p = whois_get_referral(result.c))) {
						smart_str_free(&result);
						result = whois_query(p, item TSRMLS_CC);
						efree(p);
					}
				}
				MAKE_STD_ZVAL(new_value);
				ZVAL_STRINGL(new_value, result.c, result.len, 1);
				zend_hash_update(	Z_ARRVAL_P(return_value), Z_STRVAL_PP(item),
									Z_STRLEN_PP(item) + 1, (void*)&new_value,
									sizeof(zval*), NULL
				);
				smart_str_free(&result);
			}
		}
	}

}

PHP_FUNCTION(whois_help)
{
	smart_str	result;
	zval		*help;

	MAKE_STD_ZVAL(help);
	ZVAL_STRING(help, "HELP", 0);
	result = whois_query("whois.internic.net", &help TSRMLS_CC);
	ZVAL_STRINGL(return_value, result.c, result.len, 1);
	FREE_ZVAL(help);
	smart_str_free(&result);
}

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
